package com.lazy.template;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import jakarta.annotation.Resource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

/**
 * @author fengshaoyu
 * @date 2024/1/30 14:29
 */
@Component
public class CacheTemplate {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;


    /**
     * 防止缓存穿透
     * @param key key
     * @param expire 时间
     * @param clazz 序列化对象
     * @param supplier 数据块
     * @param <T> T
     * @return T
     */
    public <T> T findCache(String key, long expire, TimeUnit unit, TypeReference<T> clazz,
                           Supplier<T> supplier) {
        Optional<Boolean> flag = Optional.ofNullable(redisTemplate.hasKey(key));
        if (flag.isPresent() && Boolean.TRUE.equals(flag.get())) {
            Object obj = redisTemplate.opsForValue().get(key);
            if (ObjectUtils.isEmpty(obj)) {
                return null;
            }
            return JSON.parseObject(String.valueOf(obj), clazz);
        }
        T result = supplier.get();
        if (ObjectUtils.isEmpty(result)) {
            redisTemplate.opsForValue().set(key, "", 3, TimeUnit.SECONDS);
            return null;
        }
        redisTemplate.opsForValue().set(key, JSON.toJSONString(result), expire, unit);
        return result;
    }


    /**
     * 此方法需要通过redis GenericJackson2JsonRedisSerializer 方式进行序列化存储对象
     * @param key key
     * @param expire 时间
     * @param unit 时间单位
     * @param supplier 数据块
     * @param <T> T
     * @return T
     */
    @SuppressWarnings("unchecked")
    public <T> T findCache(String key, long expire, TimeUnit unit, Supplier<T> supplier) {
        Optional<Boolean> exist = Optional.ofNullable(redisTemplate.hasKey(key));
        if (exist.isPresent() && Boolean.TRUE.equals(exist.get())) {
            Object obj = redisTemplate.opsForValue().get(key);
            if (ObjectUtils.isEmpty(obj)) {
                return null;
            }
            return (T) obj;
        }
        T result = supplier.get();
        if (ObjectUtils.isEmpty(result)) {
            redisTemplate.opsForValue().set(key, "", 3, TimeUnit.SECONDS);
            return null;
        }
        redisTemplate.opsForValue().set(key, result, expire, unit);
        return result;
    }
}
